package xyz.fefine;

import java.lang.reflect.Array;
import java.util.*;

/**
 * Created by feng_ on 2016/9/5.
 */
public class MainOpPrecedenceParser {

    private Lexer lexer;
    private HashMap<String,Precedence> operators;

    public static class Precedence{

        int value;
        boolean leftAssoc;      //左关系符

        public Precedence(int value, boolean leftAssoc) {
            this.value = value;
            this.leftAssoc = leftAssoc;
        }
    }

    public MainOpPrecedenceParser(Lexer lexer) {
        this.lexer = lexer;
        operators = new HashMap<>();        //运算符优先级
        operators.put("=",new Precedence(0,false));
        operators.put(">",new Precedence(1,true));
        operators.put("<",new Precedence(1,true));
        operators.put(">=",new Precedence(1,true));
        operators.put("<=",new Precedence(1,true));
        operators.put("==",new Precedence(1,true));
        operators.put("+",new Precedence(2,true));
        operators.put("-",new Precedence(2,true));
        operators.put("*",new Precedence(3,true));
        operators.put("/",new Precedence(3,true));
        operators.put("%",new Precedence(3,true));
        //operators.put("^",new Precedence(4,false));  //没用
    }



    /**
     * 向右移动
     * @param left
     * @param pre
     * @return
     * @throws ParseException
     */
    public ASTree doShift(ASTree left, int pre) throws ParseException {
        ASTLeaf op = new ASTLeaf(lexer.read());
        ASTree mid = factor();
        Precedence next;
        while ((next = nextOperator()) != null && rightAssoci(pre, next)) {
            mid = doShift(mid,next.value);
//            mid = new BinaryExpr(Arrays.asList());
        }

        return new BinaryExpr(Arrays.asList(left,op,mid));

    }

    /**
     * 是否为右结合性
     * @param pre 左
     * @param nextPre 右
     * @return 是右结合性
     */
    public boolean rightAssoci(int pre, Precedence nextPre) {
        int next = nextPre.value;
        if (pre < next) {
            return true;
        } else if(pre > next){
            return false;
        }else{
            //当优先级相等时判断其左结合性和右结合性
            return nextPre.leftAssoc;
        }
    }

    /**
     * 读取下一个操作符
     * @return
     * @throws ParseException
     */
    public Precedence nextOperator() throws ParseException{
        Token token = lexer.peek(0);
        if (token.isIdentifier()) {
            return operators.get(token.getString());
        } else {
            return null;
        }
    }






    public Token token(String name) throws ParseException{
        //清除 ; 和 \n
        clear();

        Token token = lexer.read();
//        System.out.println("readToken:"+token.getString());
        //不是标识符
        if (!(token.isIdentifier() && name.equals(token.getString()))) {
            throw new ParseException(token);
        }
        return token;
    }

    /**
     * 清除 ; 和 \n
     * @throws ParseException
     */
    public void clear() throws ParseException{
        while (isToken(";", "\\n")) {
            lexer.read();
        }
    }

    /**
     *  param : Identifier
     * @return
     * @throws ParseException
     */
    public ASTree param() throws ParseException{
        Token token = lexer.peek(0);
        if (token.isIdentifier()) {
            token = lexer.read();
            return new Name(token);
        } else {
            throw new ParseException(token);
        }
    }

    /**
     *  params : param { "," param }
     * @return
     * @throws ParseException
     */
    public ASTree params() throws ParseException {

        ASTree asTree = param();
        List<ASTree> list = new ArrayList<>();
        list.add(asTree);
        while (isToken(",")) {
            token(",");
            asTree = param();
            list.add(asTree);
        }

        return new ParameterList(list);
    }

    /**
     *  param_list : "(" [ params ] ")"
     * @return
     * @throws ParseException
     */
    public ASTree paramList() throws ParseException {

        if (isToken("(")) {
            token("(");
            //无参数时返回null
            ASTree asTree = new ParameterList(new ArrayList<>());
            Token next = lexer.peek(0);
            if (next.isIdentifier() && !next.getString().equals(")")) {
                asTree = params();
            }
            token(")");
            return asTree;

        } else {
            throw new ParseException(lexer.peek(0));
        }

    }

    /**
     *  def : "def" Identifier param_list block
     * @return
     * @throws ParseException
     */
    public ASTree def() throws ParseException {

        if (isToken("def")) {
            token("def");
            Token defName = lexer.read();
            ParameterList parameterList = (ParameterList) paramList();
            BlockStmnt blockStmnt = (BlockStmnt)block();
            return new DefStmnt(Arrays.asList(new Name(defName),parameterList,blockStmnt));
        } else {
            throw new ParseException(lexer.peek(0));
        }

    }


    /**
     *  args : expr { "," expr}
     * @return
     * @throws ParseException
     */
    public ASTree args() throws ParseException{

        ASTree asTree = expr();
        List<ASTree> list = new ArrayList<>();
        list.add(asTree);
        while (isToken(",")) {
            token(",");
            asTree = expr();
            list.add(asTree);
        }
/*        while (isToken("\\n")) {
            lexer.read();
        }*/
        return new Arguments(list);
    }
    /**
     *  elements : expr { "," expr}
     *  与args相同,只是为了区分
     * @return 语法树节点
     * @throws ParseException 翻译错误
     */
    public ASTree elements() throws ParseException{

        ASTree asTree = expr();
        List<ASTree> list = new ArrayList<>();
        list.add(asTree);
        while (isToken(",")) {
            token(",");
            asTree = expr();
            list.add(asTree);
        }

        return new ArrayLiteral(list);
    }

    /**
     * postfix : "(" [args] ")" | "[" expr "]"
     *
     * not
     *
     * @return
     * @throws ParseException
     */
    public ASTree postfix() throws ParseException {

        if (isToken("(")) {
            token("(");
            //无参的args
            ASTree asTree = new Arguments();
            //判断下面是否有args
            if (nextIsArgs()) {
                asTree = args();
            }
            token(")");
            //后面需要进行判断是否为空
            return asTree;
        } else if (isToken("[")) {

            token("[");
            ASTree asTree = expr();
            token("]");
            return new ArrayRef(Arrays.asList(asTree));

        } else {
            throw new ParseException(lexer.peek(0));
        }

    }



    /**
     * args : expr {,expr}
     * @return
     * @throws ParseException
     */
    public boolean nextIsArgs() throws ParseException{
        return nextIsExpr();
    }

    /**
     * primary : ("(" expr ")" || Number || Identifier || String ) { postfix }
     * @return
     * @throws ParseException
     */
    public ASTree primary() throws ParseException{

        ASTree asTree = null;
        if (isToken("(")) {
            token("(");
            asTree = expr();
            token(")");
        } else if(isToken("[")){
            token("[");
            asTree = elements();
            token("]");
        } else {
            Token token = lexer.read();
            if (token.isNumber()) {
                asTree = new NumberLiteral(token);
            } else if(token.isIdentifier()){
                asTree = new Name(token);
                String name = token.getString();
                switch (name) {
                    case "Array" : {
                        //数组
                        ASTree post = postfix();
                        return new ArrayLiteral(Arrays.asList(asTree, post));
                    }
                    case "return" : {
                        //增加return语句
                        ASTree returns = statement();
                        return new ReturnStmnt(Arrays.asList(returns));
                    }
                    default:
                }

            }else if(token.isString()){
                asTree = new StringLiteral(token);
            } else {
                System.out.println("->" + token.getString() + "<-");
                throw new ParseException(token);
            }
        }
        if (isToken("(","[")) {

            List<ASTree> list = new ArrayList<>();
            list.add(asTree);
            //待检验
            do {
                //next is postfix
                // function postfix will handle the bracket("(")
                // not stable
                asTree = postfix();
                if (asTree != null){
                    list.add(asTree);
                }
            }while (isToken("(","["));      //可以实现闭包
            return PrimaryExpr.create(list);
        }
        return asTree;
    }


    /**
     *  factor : "-" primary | primary
     * @return
     * @throws ParseException
     */
    public ASTree factor() throws ParseException{

        if (isToken("-")) {
//            ASTLeaf op = new ASTLeaf(token("-"));
            ASTree asTree = primary();

//            BinaryExpr binaryExpr = new BinaryExpr(Arrays.asList(op,asTree));
            return new NegativeExpr(Arrays.asList(asTree));

        } else {
            return primary();

        }
    }


    /**
     *  expr : factor { OP factor}
     *  OP : 双目运算符
     * @return
     * @throws ParseException
     */
    public ASTree expr() throws ParseException{
        ASTree asTree = factor();
        Precedence next;
        //while 待定
        while ((next = nextOperator()) != null) {
            asTree = doShift(asTree,next.value);
        }
//        clear();
        return asTree;
    }


    /**
     * 判断下一个不是statement
     * @return
     * @throws ParseException
     */
    public boolean nextIsStatement() throws ParseException{
        clear();
        Token token = lexer.peek(0);
/*        while (token != null && (token.isIdentifier() && (token.getString().equals("\\n") || token.getString().equals(";")))) {
            token = lexer.read();
            token = lexer.peek(0);
        }*/
//        System.out.println("___>"+token.getString());
        if (token == null || token.getLineNumber() == -1) {
            return false;
        }
        return true;
    }

    /**
     * block : "{" [ statement ] { ( ";" | EOL } [ statement ] } "}"
     *
     * @return
     * @throws ParseException
     */
    public ASTree block() throws ParseException{


        token("{");
        List<ASTree> list = new ArrayList<>();
        ASTree res;
        Token token;
        while (nextIsStatement()) {
            //token = lexer.read();   //  \n  ||  ;
            String p = lexer.peek(0).getString();
            if ("}".equals(p)) {
                break;
            }
            res = statement();
            list.add(res);
        }

        token("}");
        clear();
        if (list.size() == 0) {
            return new NullStmnt(list);
        }
        return new BlockStmnt(list);
    }

    /**
     * simple : expr [ args ]
     * @return
     * @throws ParseException
     */
    public ASTree simple() throws ParseException{

        ASTree asTree = expr();
        Token t = lexer.peek(0);
        //判断有问题
        if (!nextIsArgs()) {
            clear();
            return asTree;
        }
        List<ASTree> list = new ArrayList<>();
        list.add(asTree);
        list.add(args());
        return PrimaryExpr.create(list);

    }


    /**
     * update add "for"
     * statement : "if" expr block [ "else" block]
     *           | "while" expr block
     *           | "for" (expr1) (expr2) (expr3) block
     *           | simple
     * @return
     * @throws ParseException
     */
    public ASTree statement() throws ParseException{
        String con = lexer.peek(0).getString();
        switch (con)
        {
//            if (isToken("if")) {
            case "if" : {
                lexer.read();  // read if
                List<ASTree> list = new ArrayList<>();
                ASTree expr = expr();
                ASTree block = block();

                list.add(expr);
                list.add(block);
                if (isToken("else")) {
                    Token _else = lexer.read();
                    block = block();
                    list.add(block);
                }
                return new IfStmnt(list);

            }
//            else if (isToken("while")) {
            case "while" : {
                lexer.read();
                List<ASTree> list = new ArrayList<>();
                ASTree expr = expr();
                ASTree block = block();

                list.add(expr);
                list.add(block);
                return new WhileStmnt(list);

            }
//            else if (isToken("for")) {
            case "for" : {
                lexer.read();
                List<ASTree> list = new ArrayList<>();
                ASTree expr1 = expr();
                ASTree expr2 = expr();
                ASTree expr3 = expr();
                ASTree block = block();

                list.add(expr1);
                list.add(expr2);
                list.add(expr3);
                list.add(block);
                return new ForStmnt(list);

            }
//            else {
            default : {
                List<ASTree> list = new ArrayList<>();
                while (nextIsSimple() || isToken("def")) {

                    while (nextIsSimple()) {
                        //this is {simple}
                        list.add(simple());
                        clear();
                    }
                    while (isToken("def")) {
                        //this is def
                        //将def放在最前列
                        list.add(0,def());
//                        list.add(def());
                    }
                }
                return list.size() == 1 ? list.get(0) : new ASTList(list);
            }
        }
    }

    /**
     * expr -> factor -> primary
     * just judge in contains "-" | "(" | isIdentifier | String | Number
     * @return
     * @throws ParseException
     */
    public boolean nextIsExpr() throws ParseException{
        Token token = lexer.peek(0);
//        System.out.println("token:"+token.getString());
        if (token == null) {
            return false;
        }
        String name = token.getString();
        return ((token.isIdentifier() && !name.matches("\\\\n|;|}|\\)|def|while|if|for|else"))
                || token.isString() || token.isNumber());
    }

    /**
     * simple : expr
     * @return
     * @throws ParseException
     */
    public boolean nextIsSimple() throws ParseException {
        return nextIsExpr();
    }

    /**
     * programer : [def | statement]{";"|EOF}
     * @return
     * @throws ParseException
     */
    public ASTree programer() throws ParseException{
        /*List<ASTree> list = new ArrayList<>();
        while (nextIsStatement()) {
            list.add(statement());
        }
        return new ASTList(list);*/
//
//        if (isToken("def")) {
//            return def();
//        } else
        if (nextIsStatement()) {
            //将函数声明提升至最前。
            return statement();

        } else {
            return null;
        }

    }




    /**
     * 判断是否为标识符并且名字相同
     * 自动去除\n符
     * @param names 标识符名称 可变长度
     * @return
     * @throws ParseException
     */
    public boolean isToken(String... names) throws ParseException{
        Token token = lexer.peek(0);
        if (!token.isIdentifier()) {
            return false;
        }
        String ids = token.getString();
        for (String name : names) {
            if (name.equals(ids)) {
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) throws ParseException{

        Lexer lexer = new Lexer(new CodeDialog());
        MainOpPrecedenceParser ma = new MainOpPrecedenceParser(lexer);
        ASTree asTree = ma.programer();
        System.out.println(asTree);
/*        BasicEnv basicEnv = new BasicEnv();
        basicEnv = (BasicEnv)new Natives().enviorment(basicEnv);*/
        ResizableArrayEnv ra = new ResizableArrayEnv();
        ra = (ResizableArrayEnv) new Natives().enviorment(ra);
        Symbols s2 = ra.getNames();
        asTree.lookup(ra.getNames());
        Symbols s1 = ra.getNames();
        asTree.eval(ra);
        Symbols symbols = ra.getNames();
        Map<String, Object> map = symbols.getTables();
        Set<String> set = map.keySet();
        System.out.println("\n\n--------");
        set.forEach((s) -> {
            System.out.println(s + "-->" + map.get(s));
        });
    }

}


/**
def t_1(a,b)
{
     a = a + b
     b = a + b
     def fff()
    {
        i = 0
        while i == 0
        {
            i = i + 1
        }
        print i
    }
    if i == 2
    {
        i = i - 2
    }
     fff()
}
def t_2(d)
{
     c = 3
     t_1(c,d)
}
t_2(1)


 a = 1
 def t_1(b)
 {
    b = b + a
 }
 a


 def fib(n)
 {
 if n < 2
 {
 n
 }
 else
 {
 fib (n - 1) + fib (n - 2)
 }
 }
 t = currentTime()
 fib(15)
 print (currentTime() - t) + "ms"

 */